package ms.unicorn.dsl.analysis.impl;

import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;

import ms.unicorn.Diagram;
import ms.unicorn.Element;
import ms.unicorn.dsl.ILanguageElement;
import ms.unicorn.dsl.IModelLanguage;
import ms.unicorn.dsl.LanguageRegistry;
import ms.unicorn.dsl.analysis.IDslConverter;
import ms.unicorn.dsl.impl.ModelLanguage;
import ms.unicorn.dsl.runtime.ExecutionContext;
import ms.unicorn.dsl.runtime.VariableStorage;
import ms.unicorn.dsl.unicornDSL.DslConvertionScheme;
import ms.unicorn.dsl.unicornDSL.NamedProperty;
import ms.unicorn.dsl.utils.SphynxRuntimeException;

import org.eclipse.emf.ecore.EObject;
import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart;

public class DslConverter implements IDslConverter {

	private IModelLanguage language;
	private Map<String, Object> convertionCache = new HashMap<String, Object>();
	private Map<Object, WeakReference<Element>> originalCache = new HashMap<Object, WeakReference<Element>>();
	private Diagram diagram;
	private DslConvertionScheme scheme;
	
	public DslConverter(DslConvertionScheme scheme) {
		this.scheme = scheme;
	}

	public IModelLanguage getLanguage() {
		if (language == null) {
			for (EObject lang = scheme.eContainer(); lang != null; lang = lang.eContainer())
				if (lang instanceof ModelLanguage) 
					return language = (IModelLanguage)lang;
		}
		return language;
	}
	
	@SuppressWarnings({ "unchecked" })
	public <T> T getElementProperty(String name, T defvalue, ExecutionContext context) {
		
		for (EObject eprop: scheme.getProperties()) 
			if (eprop instanceof NamedProperty) {
				NamedProperty prop = (NamedProperty) eprop;
				if (prop.getName().equalsIgnoreCase(name)) {
					return (T)getLanguage().evaluate(prop.getValue(), context);
				}
			}
		return defvalue;
	}
	
	public <T> T getElementProperty(String name, T defvalue) {
		return getElementProperty(name, defvalue, getLanguage().getContext());
	}
	
	@Override
	public <T> T getConvertedDiagram() {
		return getConvertedObject(diagram);
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T> T getConvertedObject(Element domainObject) {
		Object obj = convertionCache.get(domainObject.getId());
		if (obj == null) {
			// run the corresponding rule
			ILanguageElement element = LanguageRegistry.INSTANCE.getLanguageElement(domainObject);
		
			// prepare the context
			ExecutionContext context = getLanguage().getContext().createChildContext();
			context.allocateVariable(VariableStorage.READONLY, false, "object", domainObject);
			context.allocateVariable(VariableStorage.READONLY, false, "converter", this);
			obj = getElementProperty(element.getName(), VariableStorage.EMPTY, context);
			if (obj == VariableStorage.EMPTY) 
				throw new SphynxRuntimeException(context, scheme, String.format("Convertion rule is missing: '%s'", element.getName()));
			
			convertionCache.put(domainObject.getId(), obj);
			originalCache.put(obj,  new WeakReference<Element>(domainObject));
			
			//if (domainObject instanceof Diagram) {
				//Diagram diagram = (Diagram) domainObject;
			for (Element child: domainObject.getElements())
				getConvertedObject(child);
			//}
		}
		return (T)obj;
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T extends Element> T getDomainObject(Object object) {
		WeakReference<Element> ref = originalCache.get(object);
		if (ref != null && ref.get() != null) 
			return (T)ref.get();
		else 
			originalCache.remove(object);
		return null;
	}

	@Override
	public IGraphicalEditPart getEditPart(Element element) {
		
		return null;
	}

	@Override
	public void setDiagram(Diagram e) {
		if (this.diagram == null)
			this.diagram = e;
	}

}
